package com.yeyks.search.service.hotel.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.json.JSONUtil;
import com.yeyks.common.constants.hotel.HotelSourceType;
import com.yeyks.search.constants.EsIndexConstant;
import com.yeyks.search.pojo.param.esSearch.EsHotelSearchBaseServiceSearchC2bHotelListParam;
import com.yeyks.search.pojo.param.esSearch.EsHotelSearchBaseServiceSearchC2bHotelListParamRoomParam;
import com.yeyks.search.pojo.vo.search.HotelInfoHotelUnionSearchVo;
import com.yeyks.search.pojo.vo.search.HotelInfoHotelUnionSearchVoCalendar;
import com.yeyks.search.pojo.vo.search.HotelInfoHotelUnionSearchVoHotle;
import com.yeyks.search.pojo.vo.search.HotelInfoHotelUnionSearchVoRoom;
import com.yeyks.search.service.hotel.EsHotelSearchC2bService;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.InnerHitBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.join.query.HasChildQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Service
public class EsHotelSearchC2BServiceImpl implements EsHotelSearchC2bService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    //根据条件搜索酒店,房间,房态
    @Override
    public HotelInfoHotelUnionSearchVo getList(EsHotelSearchBaseServiceSearchC2bHotelListParam param) {
        List<EsHotelSearchBaseServiceSearchC2bHotelListParamRoomParam> roomParams = param.getRoomParams();
        if (CollectionUtil.isNotEmpty(roomParams)) {
            try {
                //酒店查询条件集合
                BoolQueryBuilder boolQueryBuilderHotel = new BoolQueryBuilder();
                BoolQueryBuilder boolQueryBuilderRoom = new BoolQueryBuilder();
                //创建多嵌套查询
                for (EsHotelSearchBaseServiceSearchC2bHotelListParamRoomParam roomParam : roomParams) {
                    //酒店查询条件
                    BoolQueryBuilder boolQueryBuilderHotel1 = new BoolQueryBuilder();
                    boolQueryBuilderHotel1.must(QueryBuilders.termQuery("sourceType", roomParam.getSourceType()));
                    boolQueryBuilderHotel1.must(QueryBuilders.termQuery("sourceId", roomParam.getSourceHotelId()));
                    boolQueryBuilderHotel.should(boolQueryBuilderHotel1);
                    //房间查询条件
                    BoolQueryBuilder boolQueryBuilderRoom1 = new BoolQueryBuilder();
                    boolQueryBuilderRoom1.must(QueryBuilders.termQuery("sourceType", roomParam.getSourceType()));
                    boolQueryBuilderRoom1.must(QueryBuilders.termQuery("sourceId", roomParam.getSourceRoomId()));
                    boolQueryBuilderRoom.should(boolQueryBuilderRoom1);
                }

                //  三级内包条件
                ////入住时间
                RangeQueryBuilder dateRangeQueryBuilder = QueryBuilders.rangeQuery("date").gte(param.getStartTime()).lt(param.getEndTime());
                ////底价小于出价
                RangeQueryBuilder dateRangeQueryBottomPrice = QueryBuilders.rangeQuery("bottomPrice").lte(param.getPrice());
                ////售价大于出价
                RangeQueryBuilder dateRangeQuerySalePrice = QueryBuilders.rangeQuery("salePrice").gte(param.getPrice());
                ////bool对象
                BoolQueryBuilder boolQueryBuilderCalendar = QueryBuilders.boolQuery();
                boolQueryBuilderCalendar.must(dateRangeQueryBuilder);
                boolQueryBuilderCalendar.must(dateRangeQueryBottomPrice);
                boolQueryBuilderCalendar.must(dateRangeQuerySalePrice);

                //子文档查询条件-第三级
                HasChildQueryBuilder hasChildQueryBuilder3 = new HasChildQueryBuilder(EsIndexConstant.JOIN_TYPE_CALENDAR, boolQueryBuilderCalendar, ScoreMode.None);
                hasChildQueryBuilder3.innerHit(new InnerHitBuilder().setSize(100));

                //  二级内包条件
                BoolQueryBuilder boolQueryBuilder2 = new BoolQueryBuilder();
                boolQueryBuilder2.must(boolQueryBuilderRoom);
                boolQueryBuilder2.must(hasChildQueryBuilder3);
                //子文档查询条件-第二级
                HasChildQueryBuilder hasChildQueryBuilder2 = new HasChildQueryBuilder(EsIndexConstant.JOIN_TYPE_ROOM, boolQueryBuilder2, ScoreMode.None);
                hasChildQueryBuilder2.innerHit(new InnerHitBuilder().setSize(100));
                //集成搜索条件
                BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
                //  酒店条件
                boolQueryBuilder.must(boolQueryBuilderHotel);
                //  二级查询条件
                boolQueryBuilder.must(hasChildQueryBuilder2);

                //最终搜索条件
                SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
                searchSourceBuilder.from(0);
                searchSourceBuilder.size(500);
                searchSourceBuilder.query(boolQueryBuilder);
                //最终搜索对象
                SearchRequest searchRequest = new SearchRequest(EsIndexConstant.INDEX_HOTEL);
                searchRequest.types(EsIndexConstant.NORMAL_TYPE);
                searchRequest.source(searchSourceBuilder);

                //打印查询语句
//                RestHighLevelClientLogUtil.log(searchRequest);

                //查询
                SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

                //封装返回值
                ArrayList<HotelInfoHotelUnionSearchVoHotle> searchVoHotles = new ArrayList<>();
                SearchHits searchHits = searchResponse.getHits();
                if (searchHits.getTotalHits() > 0) {

                    //封装酒店
                    SearchHit[] hitsHotelInfoHotelUnionSearchVoHotles = searchHits.getHits();
                    for (SearchHit hitsHotelInfoHotelUnionSearchVoHotle : hitsHotelInfoHotelUnionSearchVoHotles) {
                        HotelInfoHotelUnionSearchVoHotle hotelInfoHotelUnionSearchVoHotle = JSONUtil.toBean(hitsHotelInfoHotelUnionSearchVoHotle.getSourceAsString(), HotelInfoHotelUnionSearchVoHotle.class);
                        searchVoHotles.add(hotelInfoHotelUnionSearchVoHotle);
                        //添加距离信息
                        hotelInfoHotelUnionSearchVoHotle.setDistance(Double.valueOf(GeoDistance.ARC.calculate(
                                param.getPositionY(),
                                param.getPositionX(),
                                hotelInfoHotelUnionSearchVoHotle.getLocation().getLat(),
                                hotelInfoHotelUnionSearchVoHotle.getLocation().getLon(), DistanceUnit.METERS)).intValue());

                        //封装房间
                        try {
                            ArrayList<HotelInfoHotelUnionSearchVoRoom> searchVoRooms = new ArrayList<>();
                            SearchHits searchHits1 = hitsHotelInfoHotelUnionSearchVoHotle.getInnerHits().get("room");
                            if (searchHits1.getTotalHits() > 0) {
                                for (SearchHit hitsHotelInfoHotelUnionSearchVoRoom : searchHits1.getHits()) {
                                    HotelInfoHotelUnionSearchVoRoom hotelInfoHotelUnionSearchVoRoom = JSONUtil.toBean(hitsHotelInfoHotelUnionSearchVoRoom.getSourceAsString(), HotelInfoHotelUnionSearchVoRoom.class);

                                    //封装房态
                                    try {
                                        ArrayList<HotelInfoHotelUnionSearchVoCalendar> hotelInfoHotelUnionSearchVoCalendars = new ArrayList<>();
                                        SearchHits searchHits2 = hitsHotelInfoHotelUnionSearchVoRoom.getInnerHits().get("calendar");
                                        if (searchHits2.getTotalHits() > 0) {
                                            for (SearchHit hitsEsRoomCalendarInfo : searchHits2.getHits()) {
                                                HotelInfoHotelUnionSearchVoCalendar hotelInfoHotelUnionSearchVoCalendar = JSONUtil.toBean(hitsEsRoomCalendarInfo.getSourceAsString(), HotelInfoHotelUnionSearchVoCalendar.class);
                                                hotelInfoHotelUnionSearchVoCalendars.add(hotelInfoHotelUnionSearchVoCalendar);
                                            }
                                        }
                                        //补全信息
                                        if (CollectionUtil.isNotEmpty(hotelInfoHotelUnionSearchVoCalendars)) {
                                            int minCount = hotelInfoHotelUnionSearchVoCalendars.get(0).getCount();
                                            int minPrice = hotelInfoHotelUnionSearchVoCalendars.get(0).getSalePrice();
                                            int referencePrice = hotelInfoHotelUnionSearchVoCalendars.get(0).getSalePrice();
                                            for (HotelInfoHotelUnionSearchVoCalendar hotelInfoHotelUnionSearchVoCalendar : hotelInfoHotelUnionSearchVoCalendars) {
                                                if (hotelInfoHotelUnionSearchVoCalendar.getCount() < minCount) {
                                                    minCount = hotelInfoHotelUnionSearchVoCalendar.getCount();
                                                }
                                                if (hotelInfoHotelUnionSearchVoCalendar.getSalePrice() < minPrice) {
                                                    minPrice = hotelInfoHotelUnionSearchVoCalendar.getSalePrice();
                                                }
                                                if (hotelInfoHotelUnionSearchVoCalendar.getSalePrice() > referencePrice) {
                                                    referencePrice = hotelInfoHotelUnionSearchVoCalendar.getSalePrice();
                                                }
                                            }
//                                            //第三方酒店实参参考价上浮
//                                            if (!Objects.equals(hotelInfoHotelUnionSearchVoRoom.getSourceType(), HotelSourceType.YYKS)) {
//                                                referencePrice *= 1.05;
//                                            }
                                            hotelInfoHotelUnionSearchVoRoom.setMinCount(minCount);
                                            hotelInfoHotelUnionSearchVoRoom.setMinPrice(minPrice);
                                            hotelInfoHotelUnionSearchVoRoom.setReferencePrice(referencePrice);
                                        }
                                        hotelInfoHotelUnionSearchVoRoom.setCalendars(hotelInfoHotelUnionSearchVoCalendars);
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                    searchVoRooms.add(hotelInfoHotelUnionSearchVoRoom);
                                }
                            }
                            hotelInfoHotelUnionSearchVoHotle.setRooms(searchVoRooms);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                    //排序
                    Integer sort = param.getSort();
                    if (Objects.nonNull(sort)) {
                        searchVoHotles.sort((o1, o2) -> {
                            //1智能，2距离，3价格
                            switch (sort) {
                                case 2:
                                    //距离由进到远
                                    return CompareUtil.compare(o1.getDistance(), o2.getDistance());
                                case 3:
                                    //价格由高到低
                                    Integer o1Price = o1.getRooms().get(0).getReferencePrice();
                                    for (HotelInfoHotelUnionSearchVoRoom roomInfo : o1.getRooms()) {
                                        if (o1Price > roomInfo.getReferencePrice()) {
                                            o1Price = roomInfo.getReferencePrice();
                                        }
                                    }
                                    Integer o2Price = o2.getRooms().get(0).getReferencePrice();
                                    for (HotelInfoHotelUnionSearchVoRoom roomInfo : o2.getRooms()) {
                                        if (o2Price > roomInfo.getReferencePrice()) {
                                            o2Price = roomInfo.getReferencePrice();
                                        }
                                    }
                                    return CompareUtil.compare(o2Price, o1Price);
                                case 1:
                                default:
                                    //默认智能排序,评分由高到低
                                    return CompareUtil.compare(o2.getScore(), o1.getScore());
                            }
                        });
                    }

                }
                return new HotelInfoHotelUnionSearchVo().setList(searchVoHotles);
                //JSONUtil.toBean(searchResponse.getHits().getHits()[0].getSourceAsString(), HotelInfoHotelUnionSearchVoHotle.class)
                //JSONUtil.toBean(searchResponse.getHits().getHits()[0].getInnerHits().get("room").getHits()[0].getSourceAsString(), HotelInfoHotelUnionSearchVoRoom.class)
                //JSONUtil.toBean(searchResponse.getHits().getHits()[0].getInnerHits().get("room").getHits()[0].getInnerHits().get("calendar").getHits()[0].getSourceAsString(), EsRoomCalendarInfo.class)
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return new HotelInfoHotelUnionSearchVo().setList(new ArrayList<>());
    }

}
